package cn.coisini.service.impl;

import cn.coisini.config.MinIoConfig;
import cn.coisini.model.common.dtos.Result;
import cn.coisini.model.common.enums.ResultEnum;
import cn.coisini.service.FileUploadService;
import cn.coisini.utils.AliOssUtil;
import cn.coisini.utils.IdWorker;
import cn.coisini.utils.JwtUtil;
import cn.coisini.utils.MinioUtil;
import io.minio.MinioClient;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

/**
 * @author xiangshaw
 * Description: 文件上传实现类
 */
@Service
@Log4j2
@RequiredArgsConstructor
public class FileUploadServiceImpl implements FileUploadService {

    private final AliOssUtil aliOssUtil;

    private final MinioUtil minioUtil;

    // 上传路径
    @Value("${file.upload.local.path}")
    private String uploadPath;
    // 访问地址
    @Value("${file.upload.local.url}")
    private String url;

    private final MinioClient minioClient;

    private final MinIoConfig minIoConfig;

    // 本地单文件上传
    @Override
    public Result<String> localUploadFile(MultipartFile file) {
        String fileUrl = saveFile(file);
        if (fileUrl == null) {
            return Result.error(ResultEnum.FILE_UPLOAD_FAIL);
        }
        log.info("File uploaded successfully: {}", fileUrl);
        return Result.ok(fileUrl);
    }

    // 本地多文件上传
    @Override
    public Result<List<String>> localUploadFiles(MultipartFile[] files) {
        List<String> fileUrls = new ArrayList<>();
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                return Result.error(Collections.singletonList(file.getOriginalFilename()), ResultEnum.FILE_UPLOAD_FAIL.getMessage());
            }
            String fileUrl = saveFile(file);
            if (fileUrl == null) {
                return Result.error(Collections.singletonList(file.getOriginalFilename()), ResultEnum.FILE_UPLOAD_FAIL.getMessage());
            }
            fileUrls.add(fileUrl);
        }
        return Result.ok(fileUrls);
    }

    // 保存文件的通用方法
    private String saveFile(MultipartFile file) {
        try {
            // 1.获取上传文件的原始文件名
            String originalFilename = file.getOriginalFilename();
            if (originalFilename == null) {
                return null;
            }
            // 2.生成新的文件名，使用用户ID、分布式ID、文件名 来保证生成的文件名唯一性
            String newFileName = generateFileName(originalFilename);
            // 3.构建上传文件的完整路径，相对于项目的 resources/static/upload/images/ 目录
            Path uploadFolderPath = Paths.get(uploadPath).toAbsolutePath().normalize();
            if (!Files.exists(uploadFolderPath)) {
                Files.createDirectories(uploadFolderPath);
            }
            // 4.获取文件保存的完整路径
            Path filePath = uploadFolderPath.resolve(newFileName).normalize();
            // 5.将文件保存到目标路径
            file.transferTo(filePath.toFile());
            // 6.构建文件的访问URL，相对于静态资源路径
            return url + "/images/" + newFileName;
            // 7. 保存失败，返回null
        } catch (IOException e) {
            log.error("Failed to save file: {}", e.getMessage());
            return null;
        }
    }

    private String generateFileName(String originalFilename) {
        // 生成文件名
        return JwtUtil.getUserIdFromToken() + "_" + new IdWorker().nextId() + "_" + originalFilename;
    }

    // 阿里云单文件上传
    @Override
    public Result<String> aliUploadFile(MultipartFile file) {
        // 1.判断文件是否为空
        if (file.isEmpty()) {
            return Result.error(ResultEnum.FILE_UPLOAD_FAIL, "File is empty");
        }
        // 2.获取文件输入流
        try (InputStream in = file.getInputStream()) {
            // 3.设置文件名
            String fileName = generateFileName(file.getOriginalFilename());
            // 4.调用阿里云OSS工具上传文件
            String aliOssUrl = aliOssUtil.uploadFile(fileName, in);
            // 5.上传成功，返回文件URL
            return Result.ok(aliOssUrl);
            // 6.上传失败，返回错误信息
        } catch (Exception e) {
            log.error("Failed to upload file to Aliyun OSS: {}", e.getMessage());
            return Result.error(ResultEnum.FILE_UPLOAD_FAIL, "Failed to upload file to Aliyun OSS");
        }
    }

    // 本地单文件删除
    @Override
    public Result<ResultEnum> localDeleteFile(String fileName) {
        Path filePath = Paths.get(uploadPath, fileName).toAbsolutePath().normalize();
        try {
            if (Files.deleteIfExists(filePath)) {
                log.info("File deleted successfully: {}", filePath);
                return Result.ok(ResultEnum.SUCCESS);
            } else {
                log.warn("File does not exist: {}", filePath);
                return Result.error(ResultEnum.FILE_DELETE_FAIL);
            }
        } catch (IOException e) {
            log.error("Failed to delete file: {}", e.getMessage());
            return Result.error(ResultEnum.FILE_DELETE_FAIL);
        }
    }

    // 本地多文件删除
    @Override
    public Result<List<String>> localDeleteFiles(List<String> fileNames) {
        List<String> failedDeletions = new ArrayList<>();
        for (String fileName : fileNames) {
            Result<ResultEnum> localDeleteFile = localDeleteFile(fileName);
            if (!(localDeleteFile.getCode() == 200)) {
                failedDeletions.add(fileName);
            }
        }
        if (failedDeletions.isEmpty()) {
            return Result.ok(failedDeletions, "All files deleted successfully.");
        } else {
            return Result.error(failedDeletions, "Some files could not be deleted.");
        }
    }

    // 阿里云文件删除
    @Override
    public Result<ResultEnum> aliDeleteFile(String fileName) {
        try {
            aliOssUtil.deleteFile(fileName);
            log.info("File deleted from Aliyun OSS successfully: {}", fileName);
            return Result.ok(ResultEnum.SUCCESS, "File deleted successfully from Aliyun OSS.");
        } catch (Exception e) {
            log.error("Failed to delete file from Aliyun OSS: {}", e.getMessage());
            return Result.error(ResultEnum.FILE_DELETE_FAIL, "Failed to delete file from Aliyun OSS.");
        }
    }

    // Minio文件上传
    @Override
    public Result<List<String>> minioUploadFile(MultipartFile[] files) {
        List<String> fileUrls = new ArrayList<>();
        for (MultipartFile file : files) {
            String originalFileName = file.getOriginalFilename();
            if (originalFileName == null) {
                log.warn("File name is null for one of the uploaded files.");
                continue;
            }
            // 使用 UUID + 原始文件名 生成唯一文件名
            String fileName = UUID.randomUUID() + "_" + originalFileName;
            try {
                // 上传文件并获取文件URL
                minioUtil.upload(file, fileName);
                String fileUrl = String.valueOf(minioUtil.getFileUrl(fileName));
                fileUrls.add(fileUrl);
                log.info("File uploaded successfully: {}", fileUrl);
            } catch (Exception e) {
                log.error("Failed to upload file: {}, error: {}", originalFileName, e.getMessage(), e);
            }
        }

        if (fileUrls.isEmpty()) {
            return Result.error(ResultEnum.FILE_UPLOAD_FAIL);
        }
        return Result.ok(fileUrls);
    }

    /**
     * 预览文件
     */
    @Override
    public Result<String> minioPreviewFile(String fileName) {
        try {
            return minioUtil.getFileUrl(fileName)
                    .map(Result::ok)
                    .orElseGet(() -> Result.error(ResultEnum.FILE_NOT_FOUND, "文件不存在"));
        } catch (Exception e) {
            log.error("Failed to preview file: fileName={}, error={}", fileName, e.getMessage(), e);
            return Result.error(ResultEnum.FILE_NOT_FOUND, "文件预览失败");
        }
    }

    /**
     * Minio文件下载
     */
    @Override
    public Result<InputStream> minioDownloadFile(String fileName, HttpServletResponse response) {
        try {
            minioUtil.download(response, fileName);
            return Result.ok(null);
        } catch (Exception e) {
            log.error("Failed to download file: fileName={}, error={}", fileName, e.getMessage(), e);
            return Result.error(ResultEnum.FILE_NOT_FOUND, "文件下载失败");
        }
    }

    /**
     * Minio文件删除
     */
    @Override
    public Result<ResultEnum> minioDeleteFile(String fileName) {
        try {
            boolean deleted = minioUtil.delete(fileName);
            if (deleted) {
                return Result.ok(ResultEnum.SUCCESS);
            } else {
                return Result.error(ResultEnum.FILE_NOT_FOUND, "文件删除失败");
            }
        } catch (Exception e) {
            log.error("Failed to delete file: fileName={}, error={}", fileName, e.getMessage(), e);
            return Result.error(ResultEnum.FILE_NOT_FOUND, "文件删除失败");
        }
    }
}
